Skip to content

Plan 208: kind files under .mdsmith/kinds/ - #391

Merged
jeduden merged 2 commits into
mainfrom
claude/kind-files-plan-SLVp3
May 24, 2026
Merged

Plan 208: kind files under .mdsmith/kinds/#391
jeduden merged 2 commits into
mainfrom
claude/kind-files-plan-SLVp3

Conversation

@jeduden

@jeduden jeduden commented May 24, 2026

Copy link
Copy Markdown
Owner

Implements plan 208. A kind can now live in its own YAML file at .mdsmith/kinds/<name>.{yaml,yml}; the basename is the kind's name and the body is the full KindBody (schema, rules, path-pattern:, extends:).

Summary

  • internal/config/kind_files.go — new discoverKinds(workspaceDir) walks .mdsmith/kinds/*.{yaml,yml} and validates basenames ([a-z][a-z0-9-]*), rejects subdirectories, .yaml/.yml extension collisions, and unknown top-level keys (strict decoding).
  • Load — merges file-defined kinds into cfg.Kinds and errors on dual-source name collisions, naming both sources. Inline kinds get tagged with the .mdsmith.yml path so every kind body now carries a SourcePath.
  • Provenance — threads SourcePath through LayerEntry and ResolvedKind so audit consumers can attribute each kind layer to its defining file. The pairwise schema-source mutex and the extends: cycle detector run unchanged on the merged kinds map (file kinds may extend inline kinds and vice versa).
  • CLImdsmith kinds resolve <file> prints defined-in <path> next to each kind it reports; kinds show adds a defined-in: line; both add a source-path JSON field.
  • Docs — new docs/reference/kind-files.md, boundaries-table row in docs/development/architecture/cross-system.md, and a "split a kind into its own file" recipe in docs/guides/file-kinds.md.
  • Testskind_files_test.go (unit), kind_file_contract_test.go (contract under internal/integration/), kind_file_equivalence_test.go (byte-equal diagnostics between inline and file kinds), plus three file-defined parallels of the existing schema-source-mutex tests.

Deferred

  • Task 10 (repo migration) — moving every kind out of this repo's .mdsmith.yml is deferred until the next release bumps the pinned mdsmith version (the pinned binary doesn't speak .mdsmith/kinds/ yet).
  • Task 11 (follow-up plan) — filed as plan 209 for .mdsmith/conventions/<name>.yaml.

Test plan

🤖 https://claude.ai/code/session_01Ctz5CK1Zhz99xn4RYtmDGn


Generated by Claude Code

Each YAML file at `.mdsmith/kinds/<name>.{yaml,yml}` now declares
one kind. The basename is the kind's name; the body is the full
`KindBody` (schema, rules, path-pattern, extends). Inline
`kinds.<name>:` in `.mdsmith.yml` stays a first-class source —
projects can mix file-defined and inline kinds freely. The same
name in both is a config error naming both sources so a merged
kind cannot defeat the "read one file to know one kind" property
this surface ships.

`discoverKinds` walks the directory at load time and rejects bad
basenames (`[a-z][a-z0-9-]*`), subdirectories, `.yaml`/`.yml`
extension collisions, and unknown top-level keys (strict
decoding). The merged kind layer threads through `ValidateKinds`
unchanged so the pairwise schema-source mutex and the
`extends:` cycle detector apply uniformly across sources.

Every kind body now carries a `SourcePath` populated by `Load`
(the `.mdsmith.yml` path for inline kinds, the kind-file path
for file kinds). Provenance threads that path through
`LayerEntry.SourcePath` and `ResolvedKind.SourcePath`, and
`mdsmith kinds resolve` / `kinds show` print a `defined-in` line
next to each kind they report. The JSON shape carries
`source-path` on both the body and per-resolved-kind entries.

The repository's own `.mdsmith.yml` migration is deferred:
the pinned `mdsmith` binary this repo lints itself with does
not yet support `.mdsmith/kinds/`. Migration is scheduled for
after the next release bumps the pinned version. Plan 209 is
filed as the follow-up for the parallel
`.mdsmith/conventions/<name>.yaml` slot.

https://claude.ai/code/session_01Ctz5CK1Zhz99xn4RYtmDGn
Copilot AI review requested due to automatic review settings May 24, 2026 13:39
@codecov

codecov Bot commented May 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.14%. Comparing base (c0a9a17) to head (9caa438).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
Components Coverage Δ
Go 97.11% <100.00%> (+<0.01%) ⬆️
TypeScript 99.39% <ø> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jeduden
jeduden marked this pull request as ready for review May 24, 2026 13:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements plan 208 by adding support for defining kinds in standalone YAML files under .mdsmith/kinds/<name>.{yaml,yml}, merging them into loaded config with strict decoding and provenance (SourcePath) that is surfaced in CLI/audit outputs.

Changes:

  • Add discovery + strict parsing for .mdsmith/kinds/*.{yaml,yml} and merge results into cfg.Kinds with collision checks.
  • Thread kind provenance (SourcePath) through kind resolution and CLI/JSON output surfaces.
  • Add unit + integration/contract tests and update docs/plans to document and lock the new public surface.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
plan/209_convention-files.md Adds follow-up plan for convention-per-file config.
plan/208_kind-files.md Updates plan status/tasks/acceptance criteria for kind files.
PLAN.md Updates plan catalog (208 in-progress; adds 209).
internal/config/kind_files.go Implements .mdsmith/kinds/ discovery, strict decoding, and merge into config.
internal/config/kind_files_test.go Unit tests for kind-file discovery/merge and provenance threading.
internal/config/load.go Tags inline kinds with SourcePath and merges file-defined kinds during load.
internal/config/config.go Adds KindBody.SourcePath field (provenance).
internal/config/merge.go Ensures SourcePath is preserved when copying kinds during merge.
internal/config/provenance.go Threads SourcePath through ResolvedKind and rule-layer provenance.
internal/kindsout/kindsout.go Surfaces SourcePath in text + JSON outputs (defined-in, source-path).
internal/kindsout/kindsout_test.go Tests for new defined-in/source-path output surfaces.
internal/integration/kind_file_contract_test.go Contract tests for .mdsmith/kinds/ public surface invariants.
internal/integration/kind_file_equivalence_test.go Integration test asserting diagnostics equivalence (inline vs file kind).
docs/reference/kind-files.md New reference doc for kind files layout, rules, and audit surfaces.
docs/reference/index.md Adds reference entry for kind files doc.
docs/guides/file-kinds.md Adds “split a kind into its own file” recipe.
docs/development/architecture/cross-system.md Documents .mdsmith/kinds/ as a cross-system/public surface.
CLAUDE.md Updates embedded docs catalog entry to include kind files doc.
AGENTS.md Updates embedded docs catalog entry to include kind files doc.
.github/copilot-instructions.md Updates embedded docs catalog entry to include kind files doc.

Comment thread internal/config/config.go
Comment on lines +159 to +164
// SourcePath is the workspace-absolute path of the file that
// defined this kind — either `.mdsmith.yml` for inline kinds
// or `.mdsmith/kinds/<name>.{yaml,yml}` for file-defined kinds
// (plan 208). Not serialized to YAML; populated by Load.
// Provenance surfaces it as `kinds.<name> (<path>)`; CLI
// resolve/show prints it next to each kind.
available without going through a target file.

The JSON shape (`--json`) carries a
`source-path:` key on each kind body and on every
codecov/patch reported `∅` for the Go component on PR #391 because
the new helpers had uncovered defensive branches that pulled patch
coverage below the project baseline. Close them by either driving
the branch with a test or removing it:

- `mergeKindFiles`: drop the `cfgPath == ""` early return.
  Load is the only caller and always passes a non-empty path.
  CLAUDE.md's "defensive only when you can drive it red/green"
  rule applies here — no test could realistically reach it.
- `discoverKinds`: add tests for the non-IsNotExist ReadDir
  branch (plant a regular file at `.mdsmith/kinds`), the
  non-YAML-extension skip branch (drop a `.md` alongside),
  the YAML anchor/alias rejection branch, and the
  malformed-YAML decode branch. Each test names the offending
  file in the assertion so future refactors keep the
  file-naming contract.
- `parseKindFile`: add a chmod-0000 read-error test. Skips on
  test users (root) where mode-0000 files remain readable;
  CI runs as `runner`, where the branch is exercised.
- `Load`: add a propagation test so a discovery error from a
  bad basename surfaces as the `loading kind files` wrap on
  the Load surface.
- `WriteBodyText` (kindsout): add a failingWriter test for the
  new `defined-in:` line so its error path matches the rest
  of the writer surface.

After these, the three new helpers (`discoverKinds`,
`mergeKindFiles`, `parseKindFile`) hit 100% / 100% / ~91%
(the last branch covered in CI). `WriteBodyText` is back at
100%.

https://claude.ai/code/session_01Ctz5CK1Zhz99xn4RYtmDGn
@jeduden
jeduden merged commit 91c15ca into main May 24, 2026
26 checks passed
jeduden added a commit that referenced this pull request May 28, 2026
* Mark plan 208 as complete; PR #391 merged 2026-05-24

* Add guide: choose between inline and file-based kinds

* Fold kind-form decision into file-kinds.md; drop standalone guide

Addresses /code-review findings on PR #412:

- Wrong Diatáxis type (was how-to, content is background/decision-aid).
  Folded the unique 'when to keep inline / when to lift' selection
  criteria into docs/guides/file-kinds.md as a new H2 above the
  existing 'Split a kind into its own file' section, where it sits
  next to the mechanics it points at.
- Removed the standalone guide and its catalog rows.
- Cut sections that duplicated file-kinds.md or kind-files.md:
  the YAML side-by-side examples (closed: false would have errored
  on a frontmatter-only schema), the 'what stays inline regardless'
  block, the comparison table, and the 'mixing both forms' example
  (which mislabeled this repo's inline kinds as file-based).
- Dropped the stale .mdsmith.yml line-count snapshot.
- Removed the 'maintenance decision, not a behavioral one' seesaw.
- New section uses zero em-dashes.

* Replace 'alt-tabbing' with 'switching' in file-kinds.md

Global-English pass: alt-tab is a Windows/Linux phrasal-verb idiom;
the plain verb 'switching' is keyboard-agnostic and reads on a
first pass for non-native readers.

* Address /code-review findings 1-5 on file-kinds.md

- F1: replace bare KindBody with 'body keys' (host page never defines
  the typed term; reference page expects this guide to define it).
- F2: section-end pointer now goes directly to the kind-files reference
  instead of the next H2 (which itself relayed there).
- F3: 'A PR touching X names itself' was an idiomatic personification;
  rewrite as 'carries the kind name in its path'.
- F4: trim the 'Split a kind' H2 opener that re-stated the same trigger
  the new decision-aid section above already delivered; H2 now opens
  with the action.
- F5: 'outweighs the history isolation' used an undefined noun phrase;
  expand to 'outweighs the gain of isolating each kind's edit history'.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants